home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 2008 February
/
PCWorld_2008-02_cd.bin
/
domacnost a kancelar
/
move action
/
moveaction.exe
/
Unit1.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2007-12-27
|
27KB
|
842 lines
unit Unit1;
interface
uses
math, comobj, shellapi, Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons, clipbrd,ExtCtrls, AviCaptura, MMSystem,
IdTCPConnection, IdTCPClient, IdHTTP,
Spin, JLCVideo, inifiles, mainthread, ComCtrls, jpeg,mailerthread, ftpthread;
const
WM_NOTIFYICON = WM_USER+333;
version = '2.0';
type
TForm1 = class(TForm)
Panel1: TPanel;
JLCVideo1: TJLCVideo;
pnlSpeedButtons: TPanel;
SpeedButton1: TSpeedButton;
sbCameraSource: TSpeedButton;
pnlMainImage: TPanel;
imgPrevious: TImage;
imgCurrent: TImage;
pnlControls: TPanel;
lblInformation: TLabel;
Label1: TLabel;
ProgressBar1: TProgressBar;
Label2: TLabel;
TrackBar1: TTrackBar;
lblActualMovement: TLabel;
lblMovementTrigger: TLabel;
btnCancelLock: TButton;
lblLockCountdown: TLabel;
pnlDetectionZone: TPanel;
pnlZoneImage: TPanel;
imgZone: TImage;
sbDefineZone: TSpeedButton;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure btnCancelLockClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
procedure TrackBar1Change(Sender: TObject);
procedure sbCameraSourceClick(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure FormCanResize(Sender: TObject; var NewWidth, NewHeight: Integer;
var Resize: Boolean);
procedure FormShow(Sender: TObject);
procedure drawZoneBox;
procedure ControlMouseDown(Sender: TObject;
Button: TMouseButton;
Shift: TShiftState;
X, Y: Integer);
procedure ControlMouseMove(Sender: TObject;
Shift: TShiftState;
X, Y: Integer);
procedure ControlMouseUp(Sender: TObject;
Button: TMouseButton;
Shift: TShiftState;
X, Y: Integer);
procedure sbDefineZoneClick(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
inReposition,alwaysOnTop : boolean;
oldPos : TPoint;
MailerThread: TMailer;
FTPThread: TFTPUploader;
gotBothImages, wasIconic: boolean;
lockOnMovement, saveJpegOnMovement, minimizeToTray: boolean;
source, launchOnMovement, workingDirectory, playSoundOnMovement: string;
mainThread: TMainThread;
normalFrameinterval, moveTrigger, pixelTolerance, lockTime, gracePeriod, cancelPeriod, imageCount: integer;
gracePeriodStart: DWORD;
function calculateDifference: integer;
procedure getImageFromWebcam;
procedure getImageFromHttpServer;
function getFrame: integer;
procedure updateLockCountdown;
procedure doLockStuff(movementDetected, gracePeriodPassed: boolean);
procedure doSaveJpegStuff;
procedure doPlaySoundStuff;
function LPad(s: String; nLength: integer): string ;
procedure flashTaskBar;
public
{ Public declarations }
TrayIcon: TNotifyIconData;
HMainIcon: HICON;
procedure ClickTrayIcon(var msg: TMessage); message WM_NOTIFYICON;
procedure MinimizeClick(Sender:TObject);
function doMainIteration: boolean;
end;
TSoundPlayer = class(TThread)
private
playSoundOnMovement: string;
protected
procedure Execute; override;
public
constructor create(_playSoundOnMovement: string);
destructor destroy; override;
end;
const
confFile = 'moveaction.conf';
var
Form1: TForm1;
playingSound, initialized: boolean;
implementation
{$R *.dfm}
procedure TForm1.FormActivate(Sender: TObject);
begin
if initialized then exit;
with TIniFile.Create(ExtractFilePath(Application.ExeName) + '\' + confFile) do
begin
try
self.Top := ReadInteger('main','self.Top', 100);
self.Left := ReadInteger('main','self.Left', 100);
self.Height := ReadInteger('main','self.Height',800);
self.Width := ReadInteger('main','self.Width',600);
if ReadString('main','useZone','false') = 'true' then
begin
sbDefineZone.Down := true;
pnlDetectionZone.visible := true;
pnlDetectionZone.Top := ReadInteger('main','pnlDetectionZone.Top', 87);
pnlDetectionZone.Left := ReadInteger('main','pnlDetectionZone.Left', 82);
pnlDetectionZone.Height := ReadInteger('main','pnlDetectionZone.Height',130);
pnlDetectionZone.Width := ReadInteger('main','pnlDetectionZone.Width',167);
end;
finally
free;
end;
end;
initialized := true;
end;
procedure TForm1.FormCanResize(Sender: TObject; var NewWidth,
NewHeight: Integer; var Resize: Boolean);
begin
Resize := (NewWidth > pnlControls.width) and (NewHeight >= 400);
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if mainThread <> nil then mainThread.terminate;
if MailerThread <> nil then MailerThread.terminate;
if FTPthread <> nil then FTPthread.terminate;
sleep(normalFrameinterval * 3);
with TIniFile.Create(ExtractFilePath(Application.ExeName) + '\' + confFile) do
begin
WriteInteger('main', 'moveTrigger', Trackbar1.position);
if sbDefineZone.down then
begin
WriteString('main','useZone','true');
WriteInteger('main','pnlDetectionZone.Top',pnlDetectionZone.Top);
WriteInteger('main','pnlDetectionZone.Left',pnlDetectionZone.Left);
WriteInteger('main','pnlDetectionZone.Height',pnlDetectionZone.Height);
WriteInteger('main','pnlDetectionZone.Width',pnlDetectionZone.Width);
end
else
WriteString('main','useZone','false');
WriteInteger('main','self.Top', self.Top);
WriteInteger('main','self.Left', self.Left);
WriteInteger('main','self.Height', self.Height);
WriteInteger('main','self.Width', self.Width);
free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i,val: integer;
SRec: TSearchRec;
sendviaemail, sendviaftp: boolean;
IniFile: TIniFile ;
ftpPassword: string;
begin
initialized := false;
self.caption := self.caption + ' ' + version;
// allow the zone control to be moved around at runtime
with pnlDetectionZone do
begin
OnMouseDown := ControlMouseDown;
OnMouseMove := ControlMouseMove;
OnMouseUp := ControlMouseUp;
Left := (pnlMainImage.width div 2) - (pnlDetectionZone.Width div 2);
top := (pnlMainImage.height div 2) - (pnlDetectionZone.height div 2);
end;
playingSound := false;
imgCurrent.picture.bitmap.PixelFormat := pf24bit;
imgPrevious.picture.bitmap.PixelFormat := pf24bit;
imgZone.picture.bitmap.PixelFormat := pf24bit;
gotBothImages := false;
lockTime := -1;
pnlZoneImage.parent := pnlDetectionZone;
// get highest image count of files in directory
imageCount := 0;
i := FindFirst(ExtractFilePath(Application.ExeName) + '\image_*.jpg', faAnyFile, SRec);
try
while i = 0 do
begin
val := strtoint(copy(SRec.Name, 7, 6));
if val > imagecount then imagecount := val;
i := FindNext(SRec);
end;
finally
FindClose(SRec);
end ;
IniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + '\' + confFile);
try
with IniFile do
begin
source := ReadString('main', 'source', 'webcam');
normalFrameinterval := ReadInteger('main', 'normalFrameinterval', 1000);
moveTrigger := ReadInteger('main', 'moveTrigger', 20);
pixelTolerance := ReadInteger('main', 'pixelTolerance', 20);
gracePeriod := ReadInteger('main', 'gracePeriod', 3) * 1000;
cancelPeriod := ReadInteger('main', 'cancelPeriod', 5);
lockOnMovement := ReadString('main', 'lockOnMovement', 'false') = 'true';
saveJpegOnMovement := ReadString('main', 'saveJpegOnMovement', 'false') = 'true';
launchOnMovement := ReadString('main', 'launchOnMovement', '');
workingDirectory := ReadString('main', 'workingDirectory', '');
playSoundOnMovement := ReadString('main', 'playSoundOnMovement', '');
sendviaemail := ReadString('main', 'sendViaEmail', 'false') = 'true';
sendviaftp := ReadString('main', 'sendViaFtp', 'false') = 'true';
minimizeToTray := ReadString('main', 'minimizeToTray', 'false') = 'true';
alwaysOnTop := ReadString('main', 'alwaysOnTop', 'false') = 'true';
trackbar1.Position := moveTrigger;
// if we need to email the images, start the background thread
if sendviaemail then
begin
MailerThread := TMailer.create(IniFile, imagecount);
MailerThread.FreeOnTerminate := True ;
MailerThread.resume;
end;
// if we need to FTP the images, start the background thread
if sendviaftp then
begin
ftpPassword := ReadString('main', 'ftp.password', '');
if ftpPassword = '*prompt*' then
ftpPassword := InputBox(self.caption, 'Please enter FTP password','');
FTPThread := TFTPUploader.create(IniFile, imagecount, ftpPassword);
FTPThread.FreeOnTerminate := True ;
FTPThread.resume;
end;
end;
finally
IniFile.Free;
end;
// set up "minimize to tray" stuff
if minimizeToTray then
begin
HMainIcon:=LoadIcon(MainInstance, 'MAINICON');
Shell_NotifyIcon(NIM_DELETE, @TrayIcon);
with trayIcon do
begin
cbSize := sizeof(TNotifyIconData);
Wnd := handle;
uID := 123;
uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
uCallbackMessage := WM_NOTIFYICON;
hIcon := HMainIcon;
szTip := 'Move Action';
end;
Application.OnMinimize:= MinimizeClick;
end;
sbCameraSource.visible := (source = 'webcam');
if source = 'webcam' then JLCVideo1.Activo := true;
mainThread := TMainThread.create(self, JLCVideo1);
mainThread.FreeOnTerminate := true;
mainThread.resume;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if minimizeToTray then
Shell_NotifyIcon(NIM_Delete, @TrayIcon);
end;
procedure TForm1.FormResize(Sender: TObject);
var
imageSize, imageLeft, imageTop: integer;
begin
inReposition := true;
try
// centre the controls panel
pnlControls.Left := (self.width div 2) - (pnlControls.Width div 2);
// resize & centre the images
// TImage has some properties to do this automatically
// but they result in a lot of screen flickering
imageSize := min(pnlMainImage.Width, pnlMainImage.Height)-4;
imgCurrent.Width := imageSize;
imgCurrent.Height := imageSize;
imgPrevious.Width := imageSize;
imgPrevious.Height := imageSize;
imageTop := (pnlMainImage.Height div 2) - (imageSize div 2);
imgCurrent.Top := imageTop;
imgPrevious.Top := imageTop;
imageLeft := (pnlMainImage.Width div 2) - (imageSize div 2);
imgCurrent.left := imageLeft;
imgPrevious.left := imageLeft;
drawZoneBox;
finally
inReposition := false;
end;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
if alwaysOnTop then
begin
SetWindowPos(Form1.Handle,
HWND_TOPMOST,
0, 0, 0, 0,
SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW);
end;
FormResize(self);
end;
function TForm1.getFrame: integer;
begin
// grab an image to disc and put it on the actual image window
if source = 'webcam' then
getImageFromWebcam
else
getImageFromHttpServer;
if imgCurrent.picture.bitmap.PixelFormat <> pf24bit then
begin
mainThread.terminate;
sleep(3000);
lblInformation.caption := 'Bitmap format not 24 bit!';
lblInformation.visible := true;
application.processmessages;
result := -1;
exit;
end;
if gotBothImages then
begin
// get difference between frames
result := calculateDifference;
progressbar1.Position := result;
lblActualMovement.Caption := inttostr(result);
end
else
begin
// first iteration
result := 0;
gracePeriodStart := GetTickCount;
end;
// copy image to previous image
imgPrevious.Picture:=imgCurrent.Picture;
gotBothImages := true;
end;
procedure TForm1.getImageFromWebcam;
begin
JLCvideo1.GrabarImagenDisco;
if fileexists(JLCVideo1.FicheroImagen) then
begin
imgCurrent.picture.LoadFromFile(JLCVideo1.FicheroImagen);
if pnlDetectionZone.Visible then
begin
imgZone.Picture.bitmap.Assign(imgCurrent.picture.bitmap);
imgZone.repaint;
end;
deleteFile(pchar(JLCVideo1.FicheroImagen));
end;
end;
procedure TForm1.getImageFromHttpServer;
var
Client: TIdHTTP;
imagestream : TStringStream;
jpg : TJPEGIMAGE;
bmp: TBitmap;
begin
Client:= TIdHTTP.create;
imagestream := TStringStream.Create('');
jpg := TJPEGIMAGE.Create;
bmp := TBitmap.Create;
try
client.get(source,imagestream);
imagestream.Seek(0,sofrombeginning);
jpg.LoadFromStream(imagestream);
// this is a bit weird - you can't assign the jpeg directly to
// the image without going through a TBitmap first, then it works fine
bmp.Assign(jpg);
imgCurrent.picture.Assign(bmp);
if pnlDetectionZone.Visible then
imgZone.Picture.Assign(imgCurrent.picture);
finally
if bmp <> nil then bmp.Free;
if jpg <> nil then jpg.Free;
if imagestream <> nil then imagestream.Free;
if Client <> nil then Client.free;
end;
end;
function TForm1.doMainIteration: boolean;
var
differenceBetweenFrames: integer;
movementDetected, gracePeriodPassed: boolean;
begin
result := false;
if (inReposition) or (not initialized) then exit;
try
differenceBetweenFrames := getFrame;
if differenceBetweenFrames = -1 then exit;
movementDetected := (differenceBetweenFrames >= moveTrigger);
gracePeriodPassed := (GetTickCount - gracePeriodStart > gracePeriod);
if movementDetected then
begin
if gracePeriodPassed then
begin
if (lockOnMovement) or (saveJpegOnMovement) or (length(launchOnMovement) > 0) or (length(playSoundOnMovement) > 0) then
lblInformation.Caption := 'Movement'
else
lblInformation.Caption := 'Movement (no actions defined in moveaction.conf)';
end
else
lblInformation.Caption := 'Movement (grace period)';
end;
lblInformation.Visible := movementDetected;
if lockOnMovement then doLockStuff(movementDetected, gracePeriodPassed);
if (movementDetected) and (gracePeriodPassed) and (saveJpegOnMovement) then
doSaveJpegStuff;
if (movementDetected) and (gracePeriodPassed) and (length(launchOnMovement) > 0) then
ShellExecute(self.handle, 'open', pchar(launchOnMovement), nil, pchar(workingDirectory), SW_SHOWNORMAL);
if (movementDetected) and (gracePeriodPassed) and (length(playSoundOnMovement) > 0) then
doPlaySoundStuff;
result := movementDetected;
except
on E:Exception do
begin
lblInformation.caption := 'Error: ' + E.Message;
lblInformation.Visible := true;
end;
end;
application.processmessages;
end;
procedure TForm1.doLockStuff(movementDetected, gracePeriodPassed: boolean);
begin
// start lock countdown if movement detected
// and not already started countdown
if (movementDetected) and (gracePeriodPassed) and (lockTime = -1) then
begin
lockTime := GetTickCount + (cancelPeriod * 1000);
flashTaskBar;
wasIconic := IsIconic(application.handle);
Application.Restore;
Application.BringToFront;
end;
if (lockTime <> -1) and (GetTickCount >= lockTime) then
begin
// gracePeriodStart := GetTickCount;
lockTime := -1;
LockWorkstation;
end;
updateLockCountdown;
end;
procedure TForm1.doSaveJpegStuff;
var
JpegImg: TJpegImage;
tmpFilename, actualFilename: string;
f: file;
begin
inc(imageCount);
JpegImg := TJpegImage.Create;
try
JpegImg.Assign(imgCurrent.picture.bitmap) ;
tmpFilename := 'image_' + lpad(inttostr(imageCount), 6) + '.jpg_';
actualFilename := copy(tmpFilename, 1, length(tmpFilename)-1);
lblInformation.caption := 'Saving ' + actualFilename;
lblInformation.Visible := true;
JpegImg.SaveToFile(tmpFilename) ;
finally
JpegImg.Free;
end;
// as we initially save the file with a _ prefix we need to remove this
// this is to stop the mailer thread trying to read it as the JPG unit is still writing it out
if fileExists(actualFilename) then deletefile(actualFilename);
AssignFile(f, tmpFilename);
rename(f, actualFilename);
end;
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
ShellExecute(GetDesktopWindow, 'open', PChar('http://members.lycos.co.uk/wuul/moveaction/readme.html'), nil, nil, SW_ShowNormal);
end;
procedure TForm1.sbCameraSourceClick(Sender: TObject);
begin
JLCVideo1.SeleccionarFuente;
end;
procedure TForm1.sbDefineZoneClick(Sender: TObject);
begin
pnlDetectionZone.visible := sbDefineZone.down;
if pnlDetectionZone.Visible then
begin
drawZoneBox;
if debughook = 0 then // only show this if not inside the Delphi IDE
showmessage('Position the detection zone by dragging the border; hold SHIFT to resize it');
end;
end;
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
moveTrigger := Trackbar1.Position;
lblMovementTrigger.caption := inttostr(moveTrigger);
end;
procedure TForm1.btnCancelLockClick(Sender: TObject);
begin
lockTime := -1;
updateLockCountdown;
gracePeriodStart := GetTickCount;
if wasIconic then application.minimize;
end;
function TForm1.calculateDifference: integer;
const
showZone = false; // debugging, shows the zone being monitored in red
badZoneMsg = 'Invalid detection zone, please reposition';
type
TRGBArray = ARRAY[0..32767] OF TRGBTriple; // pf24bit
pRGBArray = ^TRGBArray;
var
x,y,changedPixels: integer;
currentLine, prevLine: pRGBArray;
currentPixel, prevPixel: TRGBTriple;
startY, endY, startX, endX, pixelsCompared: integer;
begin
if pnlDetectionZone.visible then
begin
y := ((pnlDetectionZone.Top * 100) div imgCurrent.Height);
startY := (imgCurrent.picture.Height * y) div 100;
if startY < 0 then Raise Exception.Create(badZoneMsg);
y := (((pnlDetectionZone.Top + pnlDetectionZone.Height) * 100) div imgCurrent.Height);
endY := ((imgCurrent.picture.Height * y) div 100)-1;
if endY > imgCurrent.picture.Height - 1 then Raise Exception.Create(badZoneMsg);
x := (((pnlDetectionZone.Left - imgCurrent.left) * 100) div imgCurrent.Width);
startX := (imgCurrent.picture.width * x) div 100;
if startX < 0 then Raise Exception.Create(badZoneMsg);
x := (((pnlDetectionZone.Left + pnlDetectionZone.Width - imgCurrent.left) * 100) div imgCurrent.Width);
endX := ((imgCurrent.picture.width * x) div 100)-1;
if endX > imgCurrent.picture.Width - 1 then Raise Exception.Create(badZoneMsg);
pixelsCompared := (endX-startX+1) * (endY-startY+1)
end
else
begin
startY := 0;
endY := imgCurrent.picture.Height - 1;
startX := 0;
endX := imgCurrent.picture.Width - 1;
pixelsCompared := (imgCurrent.picture.Height * imgCurrent.picture.Width)
end;
changedPixels := 0;
for y := startY to endY do
begin
currentLine := imgCurrent.picture.bitmap.Scanline[y];
prevLine := imgPrevious.picture.bitmap.Scanline[y];
for x := startX to endX do
begin
currentPixel := currentLine^[x];
prevPixel := prevLine^[x];
if (abs(currentPixel.rgbtRed - prevPixel.rgbtRed) > pixelTolerance) and
(abs(currentPixel.rgbtGreen - prevPixel.rgbtGreen) > pixelTolerance) and
((abs(currentPixel.rgbtBlue - prevPixel.rgbtBlue) > pixelTolerance)) then
inc(changedPixels);
if (showZone) and ((y = startY) or (y = endY) or (x = startX) or (x = endX)) then
begin
currentLine^[x].rgbtRed := 255;
currentLine^[x].rgbtGreen := 0;
currentLine^[x].rgbtBlue := 0;
end;
end;
end;
result := (changedPixels * 100) div pixelsCompared;
if showZone then imgCurrent.Repaint;
end;
{left-pads a string}
function TForm1.LPad(s: String; nLength: integer): string ;
begin
while length(s) < nLength do
s := '0' + s ;
result := s ;
end ;
procedure TForm1.updateLockCountdown;
begin
lblLockCountdown.visible := (lockTime <> -1);
btnCancelLock.visible := lblLockCountdown.visible;
lblLockCountdown.caption := 'Locking in ' + inttostr((lockTime - GetTickCount) div 1000);
application.processmessages;
end;
procedure TForm1.MinimizeClick(Sender:TObject);
begin
Shell_NotifyIcon(NIM_Add, @TrayIcon);
hide;
{hide the taskbar button}
if IsWindowVisible(Application.Handle)
then ShowWindow(Application.Handle, SW_HIDE);
end;
procedure TForm1.ClickTrayIcon(var msg: TMessage);
begin
case msg.lparam of
WM_LBUTTONUP, WM_LBUTTONDBLCLK :
{WM_BUTTONDOWN may cause next Icon to activate if this icon is deleted -
(Icons shift left and left neighbor will be under mouse at ButtonUp time)}
begin
Application.Restore; {restore the application}
If WindowState = wsMinimized then WindowState := wsNormal; {Reset minimized state}
{Added 5/6/04 ====>} visible:=true;
SetForegroundWindow(Application.Handle); {Force form to the foreground }
Shell_NotifyIcon(NIM_Delete, @TrayIcon);
end;
end;
end;
procedure TForm1.flashTaskBar;
var
FWinfo: TFlashWInfo;
begin
if minimizeToTray then exit;
FWinfo.cbSize := 20;
FWinfo.hwnd := Application.Handle; // Handle of Window to flash
FWinfo.dwflags := FLASHW_ALL;
FWinfo.ucount := 1; // number of times to flash
FWinfo.dwtimeout := 0; // speed in ms, 0 default blink cursor rate
FlashWindowEx(FWinfo); // make it flash!
end;
procedure TForm1.ControlMouseDown(
Sender: TObject;
Button: TMouseButton;
Shift: TShiftState;
X, Y: Integer);
begin
if Sender is TWinControl then
begin
inReposition:=True;
imgZone.Visible := false;
SetCapture(TWinControl(Sender).Handle);
GetCursorPos(oldPos);
end;
end; (*ControlMouseDown*)
procedure TForm1.ControlMouseMove(
Sender: TObject;
Shift: TShiftState;
X, Y: Integer);
const
minWidth = 20;
minHeight = 20;
var
newPos: TPoint;
frmPoint : TPoint;
begin
if inReposition then
begin
with TWinControl(Sender) do
begin
GetCursorPos(newPos);
if ssShift in Shift then
begin //resize
Screen.Cursor := crSizeNWSE;
frmPoint := ScreenToClient(Mouse.CursorPos);
if frmPoint.X > minWidth then
Width := frmPoint.X;
if frmPoint.Y > minHeight then
Height := frmPoint.Y;
end
else //move
begin
Screen.Cursor := crSize;
Left := Left - oldPos.X + newPos.X;
Top := Top - oldPos.Y + newPos.Y;
oldPos := newPos;
end;
end;
end;
end; (*ControlMouseMove*)
procedure TForm1.ControlMouseUp(
Sender: TObject;
Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if inReposition then
begin
imgZone.Visible := true;
Screen.Cursor := crDefault;
ReleaseCapture;
inReposition := False;
drawZoneBox;
end;
end; (*ControlMouseUp*)
procedure TForm1.drawZoneBox;
const
margin = 5;
begin
if not pnlDetectionZone.visible then exit;
// check zone panel is not larger than the main image
if pnlDetectionZone.Height > imgCurrent.Height then pnlDetectionZone.Height := imgCurrent.Height;
if pnlDetectionZone.Width > imgCurrent.Width then pnlDetectionZone.Width := imgCurrent.Width;
// ensure zone image cannot be dragged/positioned outsize main image
if pnlDetectionZone.Top < imgCurrent.top then
pnlDetectionZone.Top := imgCurrent.top;
if pnlDetectionZone.Left < imgCurrent.left then
pnlDetectionZone.Left := imgCurrent.left;
if pnlDetectionZone.Top + pnlDetectionZone.Height > imgCurrent.Height then
pnlDetectionZone.Top := imgCurrent.Height - pnlDetectionZone.Height;
if pnlDetectionZone.Left + pnlDetectionZone.Width > (imgCurrent.left + imgCurrent.Width) then
pnlDetectionZone.Left := (imgCurrent.left + imgCurrent.Width) - pnlDetectionZone.Width;
// ensure zone image is rendered inside the zone panel correctly
pnlZoneImage.height := pnlDetectionZone.height-(margin*2);
pnlZoneImage.width := pnlDetectionZone.width-(margin*2);
pnlZoneImage.Top := margin;
pnlZoneImage.Left := margin;
imgZone.Width := imgCurrent.Width;
imgZone.Height := imgCurrent.height;
imgZone.Left := imgCurrent.left - pnlDetectionZone.left-margin;
imgZone.Top := imgCurrent.Top - pnlDetectionZone.top-margin;
end;
procedure TForm1.doPlaySoundStuff;
var
soundPlayer: TSoundPlayer;
begin
if playingSound then exit;
playingSound := true;
soundPlayer:= TSoundPlayer.create(playSoundOnMovement);
soundPlayer.FreeOnTerminate := True ;
soundPlayer.resume;
end;
constructor TSoundPlayer.create(_playSoundOnMovement: string);
begin
inherited create(true); // create but don't start running yet
playSoundOnMovement := _playSoundOnMovement;
end;
destructor TSoundPlayer.Destroy;
begin
inherited destroy;
end;
procedure TSoundPlayer.Execute;
begin
// play the sound and reset the flag
// note - to play the sound asynchronously (call returns immediately) use this:
// sndPlaySound(PChar(playSoundOnMovement), SND_NODEFAULT Or SND_ASYNC)
sndPlaySound(PChar(playSoundOnMovement), SND_NODEFAULT);
playingSound := false;
end;
end.